// MsnCoreViewerDlg.cpp : implementation file
//

#include "stdafx.h"
#include "MsnCoreViewer.h"
#include "MsnCoreViewerDlg.h"
#include "process.h"
#include "StringTree.h"
#include "stdio.h"


#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

/////////////////////////////////////////////////////////////////////////////
// CAboutDlg dialog used for App About

class CAboutDlg : public CDialog
{
public:
	CAboutDlg();

// Dialog Data
	//{{AFX_DATA(CAboutDlg)
	enum { IDD = IDD_ABOUTBOX };
	//}}AFX_DATA

	// ClassWizard generated virtual function overrides
	//{{AFX_VIRTUAL(CAboutDlg)
	protected:
	virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV support
	//}}AFX_VIRTUAL

// Implementation
protected:
	//{{AFX_MSG(CAboutDlg)
	//}}AFX_MSG
	DECLARE_MESSAGE_MAP()
};

CAboutDlg::CAboutDlg() : CDialog(CAboutDlg::IDD)
{
	//{{AFX_DATA_INIT(CAboutDlg)
	//}}AFX_DATA_INIT
}

void CAboutDlg::DoDataExchange(CDataExchange* pDX)
{
	CDialog::DoDataExchange(pDX);
	//{{AFX_DATA_MAP(CAboutDlg)
	//}}AFX_DATA_MAP
}

BEGIN_MESSAGE_MAP(CAboutDlg, CDialog)
	//{{AFX_MSG_MAP(CAboutDlg)
		// No message handlers
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CMsnCoreViewerDlg dialog

CMsnCoreViewerDlg::CMsnCoreViewerDlg(CWnd* pParent /*=NULL*/)
	: CDialog(CMsnCoreViewerDlg::IDD, pParent)
	, m_bMarkSelection(false)
{
	//{{AFX_DATA_INIT(CMsnCoreViewerDlg)
	m_properties = _T("");
	m_bMarkSelection = TRUE;
	//}}AFX_DATA_INIT
	// Note that LoadIcon does not require a subsequent DestroyIcon in Win32
	m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
	sock = NULL;
}

void CMsnCoreViewerDlg::DoDataExchange(CDataExchange* pDX)
{
	CDialog::DoDataExchange(pDX);
	//{{AFX_DATA_MAP(CMsnCoreViewerDlg)
	DDX_Control(pDX, IDC_ELEMENTS, m_elements);
	DDX_Control(pDX, IDC_LIST1, m_windows);
	DDX_Text(pDX, IDC_PROPERTIES, m_properties);
	DDX_Check(pDX, IDC_MARKSELECTION, m_bMarkSelection);
	//}}AFX_DATA_MAP
}

BEGIN_MESSAGE_MAP(CMsnCoreViewerDlg, CDialog)
	//{{AFX_MSG_MAP(CMsnCoreViewerDlg)
	ON_WM_SYSCOMMAND()
	ON_WM_PAINT()
	ON_WM_QUERYDRAGICON()
	ON_BN_CLICKED(IDC_REFRESHWND, OnRefreshwnd)
	ON_WM_DESTROY()
	ON_NOTIFY(TVN_SELCHANGED, IDC_ELEMENTS, OnSelchangedElements)
	//}}AFX_MSG_MAP
	ON_MESSAGE((WM_USER+1),OnInfo)
	ON_MESSAGE((WM_USER+2),OnWindowSelect)
	ON_MESSAGE((WM_USER+3),OnGetWindowProc)
	ON_BN_CLICKED(IDC_MARKSELECTION, &CMsnCoreViewerDlg::OnBnClickedMarkselection)
	ON_BN_CLICKED(IDC_BUTTON1, &CMsnCoreViewerDlg::OnBnClickedButton1)
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CMsnCoreViewerDlg message handlers
HANDLE g_event=NULL;

BOOL CMsnCoreViewerDlg::OnInitDialog()
{
	CDialog::OnInitDialog();

	// Add "About..." menu item to system menu.

	// IDM_ABOUTBOX must be in the system command range.
	ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);
	ASSERT(IDM_ABOUTBOX < 0xF000);

	CMenu* pSysMenu = GetSystemMenu(FALSE);
	if (pSysMenu != NULL)
	{
		CString strAboutMenu;
		strAboutMenu.LoadString(IDS_ABOUTBOX);
		if (!strAboutMenu.IsEmpty())
		{
			pSysMenu->AppendMenu(MF_SEPARATOR);
			pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);
		}
	}

	// Set the icon for this dialog.  The framework does this automatically
	//  when the application's main window is not a dialog
	SetIcon(m_hIcon, TRUE);			// Set big icon
	SetIcon(m_hIcon, FALSE);		// Set small icon
	 
	OnRefreshwnd();
	g_event = CreateEvent(NULL,FALSE,FALSE,NULL);
	CRect rect;
	GetClientRect(rect);
	rect.top =rect.bottom-32;
	rect.right = rect.left + 32;
	m_locator.Create(rect,this);
	return TRUE;  // return TRUE  unless you set the focus to a control
}

void CMsnCoreViewerDlg::OnSysCommand(UINT nID, LPARAM lParam)
{
	if ((nID & 0xFFF0) == IDM_ABOUTBOX)
	{
		CAboutDlg dlgAbout;
		dlgAbout.DoModal();
	}
	else
	{
		CDialog::OnSysCommand(nID, lParam);
	}
}

// If you add a minimize button to your dialog, you will need the code below
//  to draw the icon.  For MFC applications using the document/view model,
//  this is automatically done for you by the framework.

void CMsnCoreViewerDlg::OnPaint() 
{
	if (IsIconic())
	{
		CPaintDC dc(this); // device context for painting

		SendMessage(WM_ICONERASEBKGND, (WPARAM) dc.GetSafeHdc(), 0);

		// Center icon in client rectangle
		int cxIcon = GetSystemMetrics(SM_CXICON);
		int cyIcon = GetSystemMetrics(SM_CYICON);
		CRect rect;
		GetClientRect(&rect);
		int x = (rect.Width() - cxIcon + 1) / 2;
		int y = (rect.Height() - cyIcon + 1) / 2;

		// Draw the icon
		dc.DrawIcon(x, y, m_hIcon);
	}
	else
	{
		CDialog::OnPaint();
	}
}

// The system calls this to obtain the cursor to display while the user drags
//  the minimized window.
HCURSOR CMsnCoreViewerDlg::OnQueryDragIcon()
{
	return (HCURSOR) m_hIcon;
}

BOOL CALLBACK EnumWndProc(HWND hwnd, LPARAM lParam)
{
	HWND parent = ::GetParent(hwnd);
	//if (parent!=NULL)
	//	return TRUE;
	HWND child = FindWindowEx(hwnd,NULL,"DirectUIHWND",NULL);
	if (child==NULL)
		return TRUE;
	CString cls;
	cls.ReleaseBuffer(::GetClassName(hwnd,cls.GetBuffer(400),400));
	CString windowtitle;
	windowtitle.ReleaseBuffer(GetWindowText(hwnd,windowtitle.GetBuffer(400),400));
	CString item;
	item.Format("%x - %s - %s",hwnd,windowtitle,cls);
	((CListBox*)lParam)->AddString(item);
	return TRUE;
}

void CMsnCoreViewerDlg::OnRefreshwnd() 
{
	m_windows.ResetContent();
	CString title,final;
	//EnumWindows(EnumWndProc,(LPARAM)&m_windows);
	HWND wnd = 0;
	for (wnd = ::FindWindow("IMWindowClass",NULL); wnd!=NULL; wnd = ::FindWindowEx(NULL,wnd,"IMWindowClass",NULL))
	{
		LPTSTR titlebuf = title.GetBuffer(300);
		int len = ::GetWindowText(wnd,titlebuf,300);
		title.ReleaseBuffer(len);
		final.Format("%x - %s - IMWindowClass",wnd,titlebuf);
		m_windows.AddString(final);
	}
	wnd = NULL;
	for (wnd = ::FindWindow("MSBLWindowClass",NULL); wnd!=NULL; wnd = ::FindWindowEx(NULL,wnd,"MSBLWindowClass",NULL))
	{
		LPTSTR titlebuf = title.GetBuffer(300);
		int len = ::GetWindowText(wnd,titlebuf,300);
		title.ReleaseBuffer(len);
		final.Format("%x - %s - MSBLWindowClass",wnd,titlebuf);
		m_windows.AddString(final);
	}
	
}

#define UTRACE object->MessageBox
void __cdecl DoEnum(void* obj)
{
	CMsnCoreViewerDlg * object =(CMsnCoreViewerDlg*)obj;
	int sel = object->m_windows.GetCurSel();
	if (sel==-1)
	{
		object->m_elements.DeleteAllItems();
		object->m_elements.InsertItem("Please select a window first.");
		return;
	}
	CString str;
	object->m_windows.GetText(sel,str);
	str = str.Left(str.Find(" "));
	HWND hwnd;
	sscanf_s(str,"%x",&hwnd);
	
	CString dllpath;
	char * szDllPath = dllpath.GetBuffer(MAX_PATH);//new char[MAX_PATH];
	::GetModuleFileName(NULL,szDllPath,MAX_PATH);
	dllpath.ReleaseBuffer();
	dllpath = dllpath.Left(dllpath.ReverseFind('\\')+1);
	dllpath +="MsnCoreHelper.dll";
	szDllPath = dllpath.GetBuffer(dllpath.GetLength()+1);
	szDllPath[dllpath.GetLength()]=0;

	BOOL bDeleteDLL = FALSE;
	HMODULE hMod = GetModuleHandle(0);
	HRSRC hrSrc = FindResource(hMod,"MSNCOREHELPER",RT_RCDATA);
	if (hrSrc)
	{
		HGLOBAL hGlob = LoadResource(hMod,hrSrc);
		HANDLE hFile = CreateFile(szDllPath,GENERIC_WRITE,FILE_SHARE_READ,NULL,CREATE_ALWAYS,FILE_ATTRIBUTE_HIDDEN,NULL);
		if (hFile!=INVALID_HANDLE_VALUE)
		{
			DWORD dwSize = SizeofResource(hMod,hrSrc);
			BYTE * pVoid = (BYTE*)LockResource(hGlob);
			DWORD dwWritten;
			WriteFile(hFile,pVoid,dwSize,&dwWritten,NULL);
			CloseHandle(hFile);
			bDeleteDLL = TRUE;
		}
		FreeResource(hGlob);
	}


	if (::IsWindow(hwnd)) {
		DWORD dwProcessID;
		DWORD dwThreadID=GetWindowThreadProcessId(hwnd,&dwProcessID);
		HANDLE hProcess;
		if (hProcess=OpenProcess(PROCESS_CREATE_THREAD|PROCESS_VM_WRITE|PROCESS_VM_OPERATION|PROCESS_QUERY_INFORMATION|PROCESS_VM_READ,false,dwProcessID)) {
			//char *szDllPath=argv[1];
			void *pRemotePath=VirtualAllocEx(hProcess,NULL,lstrlenA(szDllPath)+1,MEM_COMMIT,PAGE_READWRITE);
			if (pRemotePath) {
				DWORD dwWritten;
				if (WriteProcessMemory(hProcess,pRemotePath,szDllPath,lstrlenA(szDllPath)+1,&dwWritten)) {
					FARPROC pLoadLib=GetProcAddress(GetModuleHandle(TEXT("kernel32.dll")),"LoadLibraryA");
					DWORD dwLoadLibID;
					HANDLE hJobDone = CreateEvent(NULL,TRUE,FALSE,"MSNCoreViewerJobDone");
					HANDLE hLoadLib=CreateRemoteThread(hProcess,0,0,(LPTHREAD_START_ROUTINE)pLoadLib,pRemotePath,0,&dwLoadLibID);
					if (hLoadLib) {
						WaitForSingleObject(hLoadLib,INFINITE);  // Wait until LoadLibrary returns
						if (hJobDone)
							WaitForSingleObject(hJobDone,INFINITE);						
						HANDLE hLibrary;
						if (GetExitCodeThread(hLoadLib,(DWORD *)&hLibrary) && hLibrary!=NULL) {
							TRACE("Loaded... (Handle: %.8x)\r\n",hLibrary);
							
							WaitForSingleObject(g_event,INFINITE);
							ResetEvent(g_event);
							
							FARPROC pFreeLib=GetProcAddress(GetModuleHandle(TEXT("kernel32.dll")),"FreeLibrary");
							DWORD dwFreeLibID;
							HANDLE hFreeLib=CreateRemoteThread(hProcess,0,0,(LPTHREAD_START_ROUTINE)pFreeLib,(void*)hLibrary,0,&dwFreeLibID);
							if (hFreeLib) {
								WaitForSingleObject(hFreeLib,INFINITE);
								CloseHandle(hFreeLib);
								printf("Unloaded...\r\n");
							} else UTRACE("Could not unload library\r\n");
						} else UTRACE("LoadLibrary failed\r\n");
						CloseHandle(hLoadLib);
					} 
					else 
					{
						CString szError;
						szError.Format("%d",GetLastError());
						UTRACE("Could not call LoadLibraryA in target "+szError+"\r\n");
					}
					if (hJobDone)
						CloseHandle(hJobDone);
				} else UTRACE("Could not write to Target memory\r\n");
				VirtualFreeEx(hProcess,pRemotePath,lstrlenA(szDllPath)+1,MEM_RELEASE);
			} else UTRACE("Could not allocate memory in Target\r\n");
			CloseHandle(hProcess);
		} else UTRACE("Could not open process\r\n");
	} else UTRACE("Could not find messenger window\r\n");
	dllpath.ReleaseBuffer();

	if (bDeleteDLL)
	{
		DeleteFile(szDllPath);
	}
	//delete szDllPath;
}

void CMsnCoreViewerDlg::OnOK() 
{
	m_elements.DeleteAllItems();
	
	int sel = m_windows.GetCurSel();
	if (sel==-1)
	{
		m_elements.DeleteAllItems();
		m_elements.InsertItem("Please select a window first.");
		return;
	}
	CString str;
	m_windows.GetText(sel,str);
	str = str.Left(str.Find(" "));
	HWND hwnd;
	sscanf_s(str,"%x",&hwnd);
	m_hSelectedWnd = hwnd;

	if (sock!=NULL)
		delete sock;
	/*sock = new CLocalSocket();
	sock->m_hwnd = hwnd;
	sock->m_tree=&m_elements;
	sock->Create(3422);
	sock->Listen();*/
	_beginthread(DoEnum,0,this);
}


void CMsnCoreViewerDlg::OnDestroy() 
{
	if (sock!=NULL)
		delete sock;
	CloseHandle(g_event);
	CDialog::OnDestroy();	
}

HTREEITEM GetNextTreeItem(const CTreeCtrl& treeCtrl, HTREEITEM hItem)
{
	// has this item got any children
	if (treeCtrl.ItemHasChildren(hItem))
	{
		return treeCtrl.GetNextItem(hItem, TVGN_CHILD);
	}
	else if (treeCtrl.GetNextItem(hItem, TVGN_NEXT) != NULL)
	{
		// the next item at this level
		return treeCtrl.GetNextItem(hItem, TVGN_NEXT);
	}
	else
	{
		HTREEITEM hParentItem = treeCtrl.GetNextItem(hItem,TVGN_PARENT);
		while (hParentItem !=NULL)
		{
			if (treeCtrl.GetNextItem(hParentItem,TVGN_NEXT)!=0)
			{
				return treeCtrl.GetNextItem(hParentItem,TVGN_NEXT);
			}
			hParentItem = treeCtrl.GetNextItem(hParentItem,TVGN_PARENT);
		}
	}
	return 0;
}

// Functions to expands all items in a tree control
void ExpandAll(CTreeCtrl& treeCtrl)
{
     HTREEITEM hRootItem = treeCtrl.GetRootItem();
     HTREEITEM hItem = hRootItem;

     while (hItem)
     {
          if (treeCtrl.ItemHasChildren(hItem))
          {
               treeCtrl.Expand(hItem, TVE_EXPAND);
          }
          hItem = GetNextTreeItem(treeCtrl, hItem);
     }
}

LRESULT CMsnCoreViewerDlg::OnWindowSelect(WPARAM wParam, LPARAM lParam)
{
	HWND hWnd = (HWND)lParam;
	hWnd = ::GetParent(hWnd);
	if (hWnd)
	{
		OnRefreshwnd();
		BOOL bFound = FALSE;
		for (int i = 0; i< m_windows.GetCount(); i++)
		{
			CString szText;
			m_windows.GetText(i,szText);
			szText.Left(szText.Find(" "));
			HWND hTemp = 0;
			sscanf_s(szText,"%x",&hTemp);
			if (hTemp==hWnd)
			{
				bFound = TRUE;
				m_windows.SetCurSel(i);
				OnOK();
				break;
			}
		}
		if (!bFound)
		{
			CString cls;
			cls.ReleaseBuffer(::GetClassName(hWnd,cls.GetBuffer(400),400));
			CString windowtitle;
			windowtitle.ReleaseBuffer(::GetWindowText(hWnd,windowtitle.GetBuffer(400),400));

			CString szText;
			szText.Format("%x - %s - %s",hWnd,windowtitle,cls);
			int n = m_windows.AddString(szText);
			m_windows.SetCurSel(n);
			OnOK();
		}
	}
	return 0;
}

LRESULT CMsnCoreViewerDlg::OnGetWindowProc(WPARAM wParam, LPARAM lParam)
{
	HWND hWnd = (HWND)wParam;
	if (IsWindow(hWnd))
	{
		return ::GetWindowLong(hWnd,GWL_WNDPROC);
	}
	return NULL;
}

LRESULT CMsnCoreViewerDlg::OnInfo(WPARAM wParam, LPARAM lParam)
{
	if (lParam==NULL)
		return 0;
	HANDLE hndl = OpenFileMapping(FILE_MAP_READ,FALSE,"MsnCoreViewerMemory");
	BYTE * rawdata = (BYTE*)MapViewOfFile((HANDLE)hndl,FILE_MAP_READ,0,0,0);
	BYTE * data = rawdata+4;
	DWORD len = *(DWORD*)rawdata;
	CMemFile memfile;
	memfile.Attach(data,len);
	CArchive ar(&memfile,CArchive::load);
	CStringTree sTree;
	sTree.Serialize(ar);
	m_itemarray.RemoveAll();
	m_strarray.RemoveAll();
	m_rectarray.RemoveAll();
	sTree.TreeExport(&m_elements,NULL,TRUE,m_itemarray, m_strarray,m_rectarray);
	sTree.Destroy();
	ar.Close();
	UnmapViewOfFile(rawdata);
	CloseHandle(hndl);
	SetEvent(g_event);
	ExpandAll(m_elements);
	HTREEITEM h = m_elements.GetNextItem(TVI_ROOT,TVGN_CHILD);
	m_elements.Select(h,TVGN_FIRSTVISIBLE);
	return 0;
}

void CMsnCoreViewerDlg::OnSelchangedElements(NMHDR* pNMHDR, LRESULT* pResult) 
{
	NM_TREEVIEW* pNMTreeView = (NM_TREEVIEW*)pNMHDR;

	UpdateData();

	HTREEITEM h = m_elements.GetSelectedItem();
	int i;
	for (i = 0; i<m_itemarray.GetSize(); i++)
	{
		if (m_itemarray.GetAt(i)==h)
		{
			m_properties = m_strarray.GetAt(i);
			break;
		}
	}
	if (m_hSelectedWnd && IsWindow(m_hSelectedWnd))
	{
		if (m_bMarkSelection)
		{
			CRect rect = m_rectarray.GetAt(i);
			HWND hDUI = ::FindWindowEx(m_hSelectedWnd,0,"DirectUIHWND",0);
			if (hDUI)
			{
				m_selector.Show(hDUI,rect);
			}
			else
				OutputDebugString("Could not mark selection - could not find DirectUIHWND class\r\n");

		}
		else
			OutputDebugString("Could not mark selection - option disabled\r\n");

	}
	else
	{
		OutputDebugString("Could not mark selection - window not found\r\n");
	}

	UpdateData(FALSE);

	*pResult = 0;
}

void CMsnCoreViewerDlg::OnBnClickedMarkselection()
{
	UpdateData();
	
	HTREEITEM h = m_elements.GetSelectedItem();
	int i;
	for (i = 0; i<m_itemarray.GetSize(); i++)
	{
		if (m_itemarray.GetAt(i)==h)
		{
			if (m_hSelectedWnd && IsWindow(m_hSelectedWnd))
			{
				if (m_bMarkSelection)
				{
					CRect rect = m_rectarray.GetAt(i);
					HWND hDUI = ::FindWindowEx(m_hSelectedWnd,0,"DirectUIHWND",0);
					if (hDUI)
					{
						m_selector.Show(hDUI,rect);
					}
				}
			}
		}
	}
	if (!m_bMarkSelection)
		m_selector.Hide();
}

void CMsnCoreViewerDlg::OnBnClickedButton1()
{
	CColorDialog color;
	if (color.DoModal()==IDOK)
	{
		m_selector.SetColor(color.GetColor());
	}
}
